package com.zcxk.util;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.zcxk.config.Environment;
import com.zcxk.exception.ServiceException;
import io.jsonwebtoken.*;
import io.jsonwebtoken.impl.Base64Codec;
import io.jsonwebtoken.impl.DefaultClaims;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class JwtUtils {
	private static final String[] JWT_KEYS = new String[]{"J0jKTG3hI4chg9i0l90q3An6oaGHXKZI", "R5xKe8PKoZ0LEgtUvv9Jytz9rPhKe4hj", "cHFHQHXi7oVVLJgos1bySY40CrA2CsXa", "gsY2Ksrn7Sjif6FNPXIbptbokZb3l4Xk", "Ebs2Su1YdWVMrxXUp1RvIvrui8ZnNKhm", "Q1MFI8bV1OEMPPszKI3zJFX4wceeAJbD", "GToOrdEineUutlz19MuPeycgMdzBVBYC", "fs5Ht6eVfo8ARcQPDDE5Vd9tA7gypCUW"};
	private static final String[] JWT_TOURIST_KEYS = new String[]{"6TT72jqqHQeSN4qDjFhgHW7LfGmFUXfU", "XDmba5YhycmYlqXIy2xjJQq07gPe4SGq", "bwufTbnXOANFfFAkGa3VDlwLGKcv0cBq", "DTnV6UtYio3nTn3ct6MafWYdWrfMr06N", "Pfc7z5ajhcCI2wKYSfoJdiZoZ6nnrAVG"};
	private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);

	public JwtUtils() {
	}

	public static void check(Environment environment) throws ServiceException {
		if (StrUtil.isBlank(environment.getToken())) {
			logger.error("base interceptor,check token fail,token is empty,env:{}", new Object[]{environment});
			throw new ServiceException(10005, "token校验失败");
		} else {
			Map<String, Object> hashMap = verify(environment.getToken(), environment);
			if (hashMap.get("uid") == null) {
				logger.error("base interceptor,check token fail,uid is empty,uid is not exist,token data:{},env:{}", new Object[]{JSON.toJSONString(hashMap), environment});
				throw new ServiceException(100097, "token校验失败");
			}
		}
	}

	public static Map<String, Object> verify(String token, Environment env) throws ServiceException {
		try {
			return verify(token, new TokenSigningKeyResolver(), false, env);
		} catch (SignatureException var3) {
			return verifyTourist(token, false, env);
		}
	}

	public static Map<String, Object> verify(String token, boolean allowExpired, Environment env) throws ServiceException {
		try {
			return verify(token, new TokenSigningKeyResolver(), allowExpired, env);
		} catch (SignatureException var4) {
			return verifyTourist(token, allowExpired, env);
		}
	}

	public static Map<String, Object> verifyTourist(String token, boolean allowExpired, Environment env) throws ServiceException {
		return verify(token, new TouristTokenSigningKeyResolver(), allowExpired, env);
	}

	private static Map<String, Object> verify(String token, SigningKeyResolverAdapter adapter, boolean allowExpired, Environment env) throws ServiceException {
		try {
			Jws<Claims> claimsJws = Jwts.parserBuilder().setSigningKeyResolver(adapter).build().parseClaimsJws(token);

			Map<String, Object> resultMap = new HashMap();
			Iterator iterator = ((Claims) claimsJws.getBody()).keySet().iterator();

			while (iterator.hasNext()) {
				String key = (String) iterator.next();
				resultMap.put(key, ((Claims) claimsJws.getBody()).get(key));
			}

			return resultMap;
		} catch (Exception exception) {
			if (exception instanceof ServiceException) {
				throw exception;
			} else if (exception instanceof SignatureException) {
				throw exception;
			} else if (exception instanceof ExpiredJwtException) {
				return handlerExpiredJwtException(token, allowExpired, env, exception);
			} else {
				if (exception instanceof MalformedJwtException && StrUtil.containsIgnoreCase(exception.getMessage(), "but the header does not reference a valid signature algorithm")) {
					verifyOldToken(token);
				}

				logger.error("jwts helper,verify token fail,token:{},error message:{}", new Object[]{token, exception.getMessage(), exception});
				throw new ServiceException(100088, "token校验失败");
			}
		}
	}

	public static boolean isLegal(String jwt) {
		char SEPARATOR_CHAR = 46;
		int delimiterCount = 0;
		char[] chars = jwt.toCharArray();
		int length = chars.length;

		for (int i = 0; i < length; ++i) {
			char c = chars[i];
			if (c == SEPARATOR_CHAR) {
				++delimiterCount;
			}
		}

		return delimiterCount == 2;
	}

	public static Map<String, Object> verify(String token) throws ServiceException {
		return verify(token, (Environment) null);
	}

	public static String findUid(Environment environment) throws ServiceException {
		Map<String, Object> hashMap = verify(environment.getToken(), true, environment);
		if (hashMap.get("uid") == null) {
			logger.error("jwts helper,check token fail,check token fail,uid is not exist,environment:{}", new Object[]{environment});
			throw new ServiceException(100097, "token校验失败");
		} else {
			return hashMap.get("uid").toString();
		}
	}

	private static void verifyOldToken(String token) {
		String[] ts = token.split("[.]");
		String[] var2 = ts;
		int var3 = ts.length;

		for (int var4 = 0; var4 < var3; ++var4) {
			String s = var2[var4];

			try {
				Long expTime = JSON.parseObject(Base64Codec.BASE64.decodeToString(s)).getLong("exp");
				long current = System.currentTimeMillis() / 1000L;
				if (expTime != null && current > expTime) {
					logger.warn("jwts helper,verify token fail,the token has expired on,exp time:{},current time:{}", expTime, current);
					throw new ServiceException(10005, "token校验失败");
				}
			} catch (ServiceException var9) {
				throw var9;
			} catch (Exception var10) {
			}
		}

	}

	private static Map<String, Object> handlerExpiredJwtException(String token, boolean allowExpired, Environment env, Exception e) {
		if (!allowExpired) {
			logger.warn("jwts helper,verify token fail,the token has expired on,error message:{},env:{}", e.getMessage(), env);
			throw new ServiceException(10005, "token校验失败");
		} else {
			ExpiredJwtException expiredJwtException = (ExpiredJwtException) e;
			if (!(expiredJwtException.getClaims() instanceof DefaultClaims)) {
				logger.warn("jwts helper,verify token fail,the token has expired on,error message:{},env:{}", e.getMessage(), env);
				throw new ServiceException(10005, "token校验失败");
			} else {
				Object headerEnv = expiredJwtException.getHeader().get("env");

				DefaultClaims defaultClaims = (DefaultClaims) expiredJwtException.getClaims();
				Map<String, Object> resultMap = new HashMap();
				Iterator var8 = defaultClaims.keySet().iterator();

				while (var8.hasNext()) {
					String key = (String) var8.next();
					resultMap.put(key, defaultClaims.get(key));
				}

				return resultMap;
			}
		}
	}

	public static String encrypt(Long uid) {
		// todo 这里暂时将过期时间调长一点
		long expiration = System.currentTimeMillis() / 1000L + 8886400L;
		int kid = RandomUtil.randomInt(0, JWT_KEYS.length);
		HashMap<String, Object> header = new HashMap();
		header.put("exp", expiration);
		header.put("kid", kid);
		header.put("alg", "HS256");
		HashMap<String, Object> claims = new HashMap();
		claims.put("uid", uid);
		claims.put("exp", expiration);
		SecretKey secretKey = Keys.hmacShaKeyFor(JWT_KEYS[Integer.valueOf(kid)].getBytes(StandardCharsets.UTF_8));
		JwtBuilder builder = Jwts.builder();
		builder.signWith(secretKey);
		builder.setExpiration(new Date(expiration * 1000L));
		builder.setHeaderParams(header);
		builder.addClaims(claims);
		return builder.compact();
	}

	private static class TouristTokenSigningKeyResolver extends SigningKeyResolverAdapter {
		private TouristTokenSigningKeyResolver() {
		}

    @Override
		public Key resolveSigningKey(JwsHeader header, Claims claims) {
			String keyId = header.getKeyId();
			String keyStr = JwtUtils.JWT_TOURIST_KEYS[Integer.valueOf(keyId)];
			Key key = Keys.hmacShaKeyFor(keyStr.getBytes(StandardCharsets.UTF_8));
			return key;
		}
	}

	private static class TokenSigningKeyResolver extends SigningKeyResolverAdapter {
		private TokenSigningKeyResolver() {
		}

    @Override
		public Key resolveSigningKey(JwsHeader header, Claims claims) {
			String keyId = header.getKeyId();
			String keyStr = JwtUtils.JWT_KEYS[Integer.valueOf(keyId)];
			Key key = Keys.hmacShaKeyFor(keyStr.getBytes(StandardCharsets.UTF_8));
			return key;
		}
	}
}
